/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.smarttv;

import com.hp.hpl.jena.graph.Factory;
import com.hp.hpl.jena.graph.Graph;
import com.hp.hpl.jena.graph.Node;
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.rdf.model.Model;
import edu.yonsei.iwb.smarttv.querygraph.MSTGenerateException;
import edu.yonsei.iwb.smarttv.result.NERResults;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.sparql.syntax.ElementTriplesBlock;
import com.hp.hpl.jena.util.iterator.ExtendedIterator;
import com.hp.hpl.jena.vocabulary.OWL;
import com.hp.hpl.jena.vocabulary.RDF;
import com.hp.hpl.jena.vocabulary.RDFS;
import edu.yonsei.iwb.smarttv.model.SMARTTVModel;
import edu.yonsei.iwb.smarttv.ner.NamedEntityRecognition;
import edu.yonsei.iwb.smarttv.index.OntologyIndexer;
import edu.yonsei.iwb.smarttv.query.TermAnalyzer;
import edu.yonsei.iwb.smarttv.result.NamedEntity;
import edu.yonsei.iwb.smarttv.result.NamedEntityLinkedHashMap;
import edu.yonsei.iwb.smarttv.result.NamedEntitySet;
import edu.yonsei.iwb.smarttv.result.OntSearchResults;
import edu.yonsei.iwb.smarttv.querygraph.DynamicSPARQLGenerator;
import edu.yonsei.iwb.smarttv.querygraph.OntologySearchbySPARQL;
import edu.yonsei.iwb.smarttv.classification.SearchResultOrganizer;
import edu.yonsei.iwb.smarttv.querygraph.GenerateSearchResultGraph;
import edu.yonsei.iwb.smarttv.querygraph.algorithm.ShortestPathVector;
import edu.yonsei.iwb.smarttv.querygraph.algorithm.ShortestPathVectorCandidateSet;
import edu.yonsei.iwb.smarttv.result.ResultNode;
import edu.yonsei.iwb.smarttv.result.ResultTypeInfo;
import edu.yonsei.iwb.smarttv.result.SearchResultSet;
import edu.yonsei.iwb.smarttv.util.SMARTTVLog;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.apache.lucene.store.Directory;

/**
 *
 * @author Onlyjiny
 */
public class LocalOntologySearch implements LocalOntologySearchInterface {

    private Directory index;
    private TermAnalyzer ta;
    private NamedEntityRecognition ner;
    private DynamicSPARQLGenerator dsg;
    private OntologySearchbySPARQL oss;
    private SearchResultOrganizer sro;
    private GenerateSearchResultGraph gsrg;
    private Model model;
    private SMARTTVModel tvModel;
    private final int NUMBER_OF_CANDIDATES = 1;

    public LocalOntologySearch(SMARTTVModel model) {
        OntologyIndexer oi = new OntologyIndexer();
        this.index = oi.getIndex();
        this.model = model.getJenaModel();
        this.ta = TermAnalyzer.getTermAnalyzer();
        this.ner = new NamedEntityRecognition(this.index, this.model);
        this.dsg = new DynamicSPARQLGenerator(model);
        this.oss = new OntologySearchbySPARQL(this.model);
        this.sro = new SearchResultOrganizer(this.model);
        this.tvModel = model;
    }

    public LocalOntologySearch(SMARTTVModel model, Directory index) {
        this.model = model.getJenaModel();
        this.ta = TermAnalyzer.getTermAnalyzer();
        this.index = index;
        this.ner = new NamedEntityRecognition(this.index, this.model);
        this.dsg = new DynamicSPARQLGenerator(model);
        this.oss = new OntologySearchbySPARQL(this.model);
        this.sro = new SearchResultOrganizer(this.model);
        this.tvModel = model;
    }

    private NamedEntityLinkedHashMap recognize(String query) {
        Vector<String> indexTerms = ta.getQueryTerms(query);
        return ner.recognizeEntity(indexTerms);
    }

    public NERResults recognizeNamedEntity(String query) {
        SMARTTVLog.log(SMARTTVLog.BASIC, "Original Query String: " + query + "\n");
        NamedEntityLinkedHashMap neh = this.recognize(query);
        NERResults result = new NERResults(query, neh);
        SMARTTVLog.log(SMARTTVLog.BASIC, result.toString() + "\n");
        return result;
    }

    public NERResults recognizeNamedEntity(String query, String targetClass) {
        return this.recognizeNamedEntity(query + " " + targetClass);
    }

    public NERResults recognizeNamedEntity(String query, Resource targetClass) {
        SMARTTVLog.log(SMARTTVLog.BASIC, "Original Query String: " + query + " " + targetClass.getLocalName() + "\n");
        NamedEntityLinkedHashMap neh = this.recognize(query);
        NamedEntity ne = new NamedEntity(targetClass);
        NamedEntitySet nes = new NamedEntitySet();
        nes.add(ne);
        neh.put(targetClass.getLocalName(), nes);
        NERResults result = new NERResults(query + " " + targetClass.getLocalName(), neh);
        SMARTTVLog.log(SMARTTVLog.BASIC, result.toString() + "\n");
        return result;
    }

    public Vector<Query> generateSPARQL(NERResults ner) {
        Vector<Query> sparqlSet = new Vector<Query>();
        ShortestPathVectorCandidateSet spvcs = dsg.dynamicSPARQLGenerator(ner);
        if (!spvcs.isEmpty()) {
            Iterator<ShortestPathVector> iter = spvcs.iterator();
            while (iter.hasNext()) {
                ShortestPathVector spv = iter.next();
                Query sparql = dsg.generateSPARQL(spv);
                sparqlSet.add(sparql);
            }
            return sparqlSet;
        } else {
            return null;
        }
    }

    private Vector<Query> generateCandidateSPARQL(ShortestPathVectorCandidateSet spvcs) {
        Vector<Query> sparqlSet = new Vector<Query>();
        Iterator<ShortestPathVector> iter = spvcs.iterator();
        while (iter.hasNext() && sparqlSet.size() < this.NUMBER_OF_CANDIDATES) {
            SMARTTVLog.log(SMARTTVLog.DETAIL, "Generate candidate SPARQL\n");
            ShortestPathVector spv = iter.next();
            Query sparql = dsg.generateSPARQL(spv);
            sparqlSet.add(sparql);
        }
        return sparqlSet;
    }

    public Vector<SearchResultSet> getSearchResult(Query sparql) {
        return oss.getSPARQLResult(sparql);
    }

    private void getClassificationResult(Vector<SearchResultSet> srs) {
        this.sro.getClassificationInfo(srs);
    }
   

    public OntSearchResults searchLocalOntology(String query) {
        NERResults result = this.recognizeNamedEntity(query);
        Vector<SearchResultSet> srs = new Vector<SearchResultSet>();
        OntSearchResults osr = null;
        if (this.verifyNERReusult(result)) {
            ShortestPathVectorCandidateSet spvcs = this.dsg.dynamicSPARQLGenerator(result);            
            spvcs.printTemporalResult();
            
            Query sparql = null;
            if (!spvcs.isEmpty()) {
                while (true) {
                    sparql = this.dsg.generateSPARQL(spvcs.firstElement());
                    srs = this.getSearchResult(sparql);
                    if (!srs.isEmpty()) {
                        spvcs.removeElementAt(0);
                        break;
                    } else {
                        spvcs.removeElementAt(0);
                        if (spvcs.isEmpty()) {
                            break;
                        }
                    }
                    SMARTTVLog.log(SMARTTVLog.DETAIL, "Result is not Found, Next candidate ShortestPath Set : \n" + spvcs.firstElement().toString());
                }
            }
            Vector<Query> sparqlSet = this.generateCandidateSPARQL(spvcs);
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, sparql, sparqlSet, srs);
            
        } else {
            Iterator<String> keyIterator = result.getNamedEntityResults().keySet().iterator();
            while (keyIterator.hasNext()) {
                String key = keyIterator.next();
                NamedEntitySet nes = result.getNamedEntitySetByString(key);
                for (int i = 0; i < nes.size(); i++) {
                    SearchResultSet sr = new SearchResultSet();
                    sr.setResult(nes.get(i).getNamedEntityNode());
                    sr.setResultTypes(nes.get(i).getResultTypes());
                    srs.add(sr);
                }
            }
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, null, null, srs);
        }
        SMARTTVLog.log(SMARTTVLog.BASIC, osr.toString() + "\n");
        return osr;

    }

    public OntSearchResults searchLocalOntology(String query, String targetClass) {
        NERResults result = this.recognizeNamedEntity(query, targetClass);
        Vector<SearchResultSet> srs = new Vector<SearchResultSet>();
        OntSearchResults osr;
        if (this.verifyNERReusult(result)) {
            ShortestPathVectorCandidateSet spvcs = dsg.dynamicSPARQLGenerator(result);
            Query sparql = null;
            if (!spvcs.isEmpty()) {
                while (true) {
                    sparql = dsg.generateSPARQL(spvcs.firstElement());
                    srs = this.getSearchResult(sparql);
                    if (!srs.isEmpty()) {
                        spvcs.removeElementAt(0);
                        break;
                    } else {
                        spvcs.removeElementAt(0);
                        if (spvcs.isEmpty()) {
                            break;
                        }
                    }
                    SMARTTVLog.log(SMARTTVLog.DETAIL, "Result is not Found, Next candidate ShortestPath Set : \n" + spvcs.firstElement().toString());
                }
            }
            Vector<Query> sparqlSet = this.generateCandidateSPARQL(spvcs);
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, sparql, sparqlSet, srs);
        } else {
            Iterator<String> keyIterator = result.getNamedEntityResults().keySet().iterator();
            while (keyIterator.hasNext()) {
                String key = keyIterator.next();
                NamedEntitySet nes = result.getNamedEntitySetByString(key);
                for (int i = 0; i < nes.size(); i++) {
                    SearchResultSet sr = new SearchResultSet();
                    sr.setResult(nes.get(i).getNamedEntityNode());
                    sr.setResultTypes(nes.get(i).getResultTypes());
                    srs.add(sr);
                }
            }
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, null, null, srs);
        }
        SMARTTVLog.log(SMARTTVLog.BASIC, osr.toString() + "\n");
        return osr;
    }

    public OntSearchResults searchLocalOntology(String query, Resource targetClass) {
        NERResults result = this.recognizeNamedEntity(query, targetClass);
        Vector<SearchResultSet> srs = new Vector<SearchResultSet>();
        OntSearchResults osr;
        if (this.verifyNERReusult(result)) {
            ShortestPathVectorCandidateSet spvcs = dsg.dynamicSPARQLGenerator(result);
            Query sparql = null;
            if (!spvcs.isEmpty()) {
                while (true) {
                    sparql = dsg.generateSPARQL(spvcs.firstElement());
                    srs = this.getSearchResult(sparql);
                    if (!srs.isEmpty()) {
                        spvcs.removeElementAt(0);
                        break;
                    } else {
                        spvcs.removeElementAt(0);
                        if (spvcs.isEmpty()) {
                            break;
                        }
                    }
                    SMARTTVLog.log(SMARTTVLog.DETAIL, "Result is not Found, Next candidate ShortestPath Set : \n" + spvcs.firstElement().toString());
                }
            }
            Vector<Query> sparqlSet = this.generateCandidateSPARQL(spvcs);
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, sparql, sparqlSet, srs);      
        } else {
            Iterator<String> keyIterator = result.getNamedEntityResults().keySet().iterator();
            while (keyIterator.hasNext()) {
                String key = keyIterator.next();
                NamedEntitySet nes = result.getNamedEntitySetByString(key);
                for (int i = 0; i < nes.size(); i++) {
                    SearchResultSet sr = new SearchResultSet();
                    sr.setResult(nes.get(i).getNamedEntityNode());
                    sr.setResultTypes(nes.get(i).getResultTypes());
                    srs.add(sr);
                }
            }
            this.getClassificationResult(srs);
            osr = new OntSearchResults(query, null, null, srs);
        }
        SMARTTVLog.log(SMARTTVLog.BASIC, osr.toString() + "\n");
        return osr;
    }

    public Vector<SearchResultSet> searchLocalOntology(Graph queryGraph) {
        Vector<SearchResultSet> srs = new Vector<SearchResultSet>();
        Query query = new Query();
        ElementTriplesBlock etb = new ElementTriplesBlock();
        ExtendedIterator<Triple> ex = queryGraph.find(null, null, null);
        while (ex.hasNext()) {
            Triple t = ex.next();
            etb.addTriple(t);
        }

        List<Node> list = queryGraph.queryHandler().subjectsFor(RDF.type.asNode(), Node.ANY).toList();
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i).isVariable()) {
                query.setQuerySelectType();
                query.addResultVar(list.get(i));
                query.setQueryPattern(etb);
                break;
            }
        }
        srs = this.getSearchResult(query);
        this.getClassificationResult(srs);
        SMARTTVLog.log(SMARTTVLog.DETAIL, "\nGenerated SPAQRL based on the Graph: \n" + query.serialize());
        SMARTTVLog.log(SMARTTVLog.DETAIL, "Search Local Ontology Result by Graph Query: \n" + srs.toString());
        return srs;
    }

    // added by ETRI	
    public OntSearchResults searchLocalOntology(NERResults ner) {
        Vector<SearchResultSet> srs = new Vector<SearchResultSet>();
        OntSearchResults osr;
        if (this.verifyNERReusult(ner)) {
            ShortestPathVectorCandidateSet spvcs = dsg.dynamicSPARQLGenerator(ner);
            Query sparql = null;
            if (!spvcs.isEmpty()) {
                while (true) {
                    sparql = dsg.generateSPARQL(spvcs.firstElement());
                    srs = this.getSearchResult(sparql);
                    if (!srs.isEmpty()) {
                        spvcs.removeElementAt(0);
                        break;
                    } else {
                        spvcs.removeElementAt(0);
                        if (spvcs.isEmpty()) {
                            break;
                        }
                    }
                    SMARTTVLog.log(SMARTTVLog.DETAIL, "Result is not Found, Next candidate ShortestPath Set : \n" + spvcs.firstElement().toString());
                }
            }
            Vector<Query> sparqlSet = this.generateCandidateSPARQL(spvcs);
            this.getClassificationResult(srs);
            osr = new OntSearchResults(ner.getQuery(), sparql, sparqlSet, srs);
        } else {
            Iterator<String> keyIterator = ner.getNamedEntityResults().keySet().iterator();
            while (keyIterator.hasNext()) {
                String key = keyIterator.next();
                NamedEntitySet nes = ner.getNamedEntitySetByString(key);
                for (int i = 0; i < nes.size(); i++) {
                    SearchResultSet sr = new SearchResultSet();
                    sr.setResult(nes.get(i).getNamedEntityNode());
                    sr.setResultTypes(nes.get(i).getResultTypes());
                    srs.add(sr);
                }
            }
            this.getClassificationResult(srs);
            osr = new OntSearchResults(ner.getQuery(), null, null, srs);
        }
        SMARTTVLog.log(SMARTTVLog.BASIC, osr.toString() + "\n");
        return osr;
    }

    public Vector<Graph> getGraph(String sparql) {
        Vector<Graph> graphSet = new Vector<Graph>();
        if (sparql != null) {
            Query query = QueryFactory.create(sparql);
            this.gsrg = new GenerateSearchResultGraph(this.tvModel, query);
            graphSet = this.gsrg.generateSearchResultGraph();
        } else {
        }
        return graphSet;
    }

    public Vector<Graph> getGraph(Query sparql) {
        Vector<Graph> graphSet = new Vector<Graph>();
        if (sparql != null) {
            Query query = QueryFactory.create(sparql);
            this.gsrg = new GenerateSearchResultGraph(this.tvModel, query);
            graphSet = this.gsrg.generateSearchResultGraph();
        } else {
        }
        return graphSet;
    }
    
    /**
     * NER 결과에 Media 타입의 인스턴스만이 존재하는지 여부를 판별
     * @param ner NER 결과
     * @return Media 타입 인스턴스만 존재 : return true
     */
    private boolean verifyNERReusult(NERResults ner) {
        Iterator<String> keyIterator = ner.getNamedEntityResults().keySet().iterator();
        boolean instanceOrNot = true;
        while (keyIterator.hasNext()) {
            String key = keyIterator.next();
            NamedEntitySet nes = ner.getNamedEntitySetByString(key);
            for (int i = 0; i < nes.size(); i++) {
                NamedEntity ne = nes.get(i);
                ResultTypeInfo rti = ne.getResultTypes();
                if (rti.contains(SMARTTVModel.TARGET_CLASS_URI)) {
                    instanceOrNot = false;
                } else {
                    return true;
                }
            }
        }

        if (instanceOrNot) {
            return true;
        } else {
            return false;
        }
    }
}
